package org.jdarwinbots.logic;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Stack;

public class DefaultOperators {

	
	private static class IntPush extends Operator {

		private int num;
		private boolean isReference;

		public IntPush(int i, boolean references) {
			this.num = i;
			this.isReference = references;
		}

		@Override
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			if (isReference) {
				if (num >= 0 && num < memory.length) {
					intstack.push(memory[num]);
				} else
					intstack.push(0);
			} else
				intstack.push(num);
		}

		@Override
		public String toString() {
			return "IntPush [isReference=" + isReference + ", num=" + num + "]";
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + (isReference ? 1231 : 1237);
			result = prime * result + num;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			IntPush other = (IntPush) obj;
			if (isReference != other.isReference)
				return false;
			if (num != other.num)
				return false;
			return true;
		}
		
		

	}

	private static Map<Integer, IntPush> intOperators = new HashMap<Integer, IntPush>();
	private static Map<Integer, IntPush> refOperators = new HashMap<Integer, IntPush>();

	public static Operator getIntOperator(int key) {
		if (intOperators.containsKey(key)) {
			return intOperators.get(key);
		} else {
			IntPush newval = new IntPush(key, false);
			intOperators.put(key, newval);
			return newval;
		}
	}

	public static Operator getRefOperator(int key) {
		if (refOperators.containsKey(key)) {
			return refOperators.get(key);
		} else {
			IntPush newval = new IntPush(key, true);
			refOperators.put(key, newval);
			return newval;
		}
	}

	// Integer Operators

	private static Operator add = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(a + b);
		}
		
		public String toString(){
			return "add";
		}
		
	};

	private static Operator sub = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b - a);
		}
		
		public String toString(){
			return "sub";
		}
	};

	private static Operator mult = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(a * b);
		}
		
		public String toString(){
			return "mult";
		}
	};

	private static Operator div = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			if (a == 0 || b == 0)
				intstack.push(0);
			else
				intstack.push(b / a);
		}
		
		public String toString(){
			return "div";
		}
	};

	private static Operator mod = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			if (a == 0 || b == 0)
				intstack.push(0);
			else
				intstack.push(b % a);
		}
		
		public String toString(){
			return "mod";
		}
	};

	private static Random rand = new Random();
	private static Operator rnd = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(rand.nextInt(a - b + 1) + b);

		}
		
		public String toString(){
			return "rnd";
		}
	};

	private static Operator sgn = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			if (a == 0) {
				intstack.push(0);
			} else if (a < 0) {
				intstack.push(-1);
			} else if (a > 0) {
				intstack.push(1);
			}
		}
		
		public String toString(){
			return "sgn";
		}
	};

	private static Operator dup = new Operator() {

		@Override
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			intstack.push(a);
			intstack.push(a);
		}

		public String toString(){
			return "dup";
		}
	};

	private static Operator angle = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push((int) (-100 * (Math.atan2(b, a) - Math.PI)));
		}
		public String toString(){
			return "angle";
		}
	};

	private static Operator sqr = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
				intstack.push((int) (Math.sqrt(Math.abs(a))));
		}
		public String toString(){
			return "sqr";
		}
	};

	private static Operator abs = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			intstack.push(Math.abs(this.getNextInt(intstack)));
		}
		public String toString(){
			return "abs";
		}
	};

	private static Operator floor = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			intstack.push(Math.min(this.getNextInt(intstack), this
					.getNextInt(intstack)));
		}
		public String toString(){
			return "floor";
		}
	};

	private static Operator ceil = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			intstack.push(Math.max(this.getNextInt(intstack), this
					.getNextInt(intstack)));
		}
		public String toString(){
			return "ceil";
		}
	};

	private static Operator pyth = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push((int) Math.sqrt(a * a + b * b));
		}
		public String toString(){
			return "pyth";
		}
	};

	private static Operator pow = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push((int) Math.pow(b, a));
		}
		public String toString(){
			return "pow";
		}
	};

	private static Operator drop = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			this.getNextInt(intstack);
		}
		public String toString(){
			return "drop";
		}
	};

	private static Operator swap = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(a);
			intstack.push(b);
		}
		public String toString(){
			return "swap";
		}
	};

	private static Operator over = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b);
			intstack.push(a);
			intstack.push(b);
		}
		public String toString(){
			return "over";
		}
	};

	// storage operators
	private static Operator store = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			if(this.peekBool(boolstack)==false)return;
			int location = this.getNextInt(intstack);
			if(location>= 0 && location<memory.length){
				memory[location] = this.getNextInt(intstack);
			}
		}

		public String toString(){
			return "store";
		}
	};
	
	private static Operator inc = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			if(this.peekBool(boolstack)==false)return;
			int location = this.getNextInt(intstack);
			if(location>= 0 && location<memory.length){
				memory[location]++;
			}
		}
		public String toString(){
			return "inc";
		}
	};
	
	private static Operator dec = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			if(this.peekBool(boolstack)==false)return;
			int location = this.getNextInt(intstack);
			if(location>= 0 && location<memory.length){
				memory[location]--;
			}
		}

		public String toString(){
			return "dec";
		}
	};
	
	//bitwise operators
	private static Operator BW_compl = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			intstack.push(~this.getNextInt(intstack));
			
		}

		public String toString(){
			return "~";
		}
	};
	
	private static Operator BW_left = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b<<a);
		}

		public String toString(){
			return "<<";
		}
	};
	
	private static Operator BW_right = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b>>a);
		}

		public String toString(){
			return ">>";
		}
	};
	
	private static Operator BW_and = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b&a);
		}

		public String toString(){
			return "&";
		}
	};
	
	private static Operator BW_or = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b|a);
		}

		public String toString(){
			return "|";
		}
	};
	
	private static Operator BW_xor = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			intstack.push(b^a);
		}

		public String toString(){
			return "^";
		}
	};
	
	private static Operator neg = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = -1*this.getNextInt(intstack);
			intstack.push(a);
		}
		
		public String toString(){
			return "-";
		}

	};
	
	private static Operator incval = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			intstack.push(a+1);
		}

		public String toString(){
			return "++";
		}
	};
	
	private static Operator decval = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			intstack.push(a-1);
		}

		public String toString(){
			return "--";
		}
	};
	
	//Flow Operators
	private static Operator cond = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.clear();
		}
		
		public String toString(){
			return "cond";
		}

	};
	
	private static Operator start = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean flag = true;
			while(!boolstack.isEmpty()){
				flag = flag && boolstack.pop();
			}
			boolstack.push(flag);
		}

		public String toString(){
			return "start";
		}
	};
	
	private static Operator stop = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.clear();
			intstack.clear();
		}

		public String toString(){
			return "stop";
		}
	};
	
	private static Operator clear = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			intstack.clear();
		}

		public String toString(){
			return "clear";
		}
	};
	
	private static Operator clearbool = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.clear();
		}

		public String toString(){
			return "clearbool";
		}
	};
	
	//Boolean Operators
	
	private static Operator less = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)>this.getNextInt(intstack));
		}

		public String toString(){
			return "<";
		}
	};
	
	private static Operator greater = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)<this.getNextInt(intstack));
		}

		public String toString(){
			return ">";
		}
	};
	
	private static Operator less_or_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)>=this.getNextInt(intstack));
		}

		public String toString(){
			return "<=";
		}
	};
	
	private static Operator greater_or_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)<=this.getNextInt(intstack));
		}

		public String toString(){
			return ">=";
		}
	};
	
	private static Operator equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)==this.getNextInt(intstack));
		}

		public String toString(){
			return "=";
		}
	};
	
	private static Operator not_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(this.getNextInt(intstack)!=this.getNextInt(intstack));
		}

		public String toString(){
			return "!=";
		}
	};
	
	private static Operator approx_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			
			boolstack.push(Math.abs(b-a)<=Math.abs(a/10));
			
		}

		public String toString(){
			return "%=";
		}
	};
	
	private static Operator approx_not_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			
			boolstack.push(!(Math.abs(b-a)<=Math.abs(a/10)));
			
		}
		public String toString(){
			return "!%=";
		}
	};
	
	private static Operator custom_approx_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int c = this.getNextInt(intstack);
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			
			boolstack.push(Math.abs(b-a)<=Math.abs(a*c/100));
			
		}

		public String toString(){
			return "~=";
		}
	};
	
	private static Operator custom_approx_not_equal = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			int c = this.getNextInt(intstack);
			int a = this.getNextInt(intstack);
			int b = this.getNextInt(intstack);
			
			boolstack.push(!(Math.abs(b-a)<=Math.abs(a*c/100)));
			
		}

		public String toString(){
			return "!~=";
		}
	};
	
	private static Operator and = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolean b = this.getNextBool(boolstack);
			
			boolstack.push(a&&b);
			
		}

		public String toString(){
			return "and";
		}
	};
	
	private static Operator or = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolean b = this.getNextBool(boolstack);
			
			boolstack.push(a||b);
			
		}

		public String toString(){
			return "or";
		}
	};
	
	private static Operator xor = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolean b = this.getNextBool(boolstack);
			
			boolstack.push(a^b);
			
		}

		public String toString(){
			return "xor";
		}
	};
	
	private static Operator not = new Operator() {

		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			
			boolstack.push(!a);
			
		}
		public String toString(){
			return "not";
		}
	};
	
	private static Operator dropbool = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			this.getNextBool(boolstack);
		}
		public String toString(){
			return "dropbool";
		}
	};
	
	private static Operator dupbool = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolstack.push(a);
			boolstack.push(a);
		}
		public String toString(){
			return "dupbool";
		}
	};
	
	private static Operator swapbool = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolean b = this.getNextBool(boolstack);
			boolstack.push(a);
			boolstack.push(b);
		}
		public String toString(){
			return "swapbool";
		}
	};
	
	private static Operator overbool = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolean a = this.getNextBool(boolstack);
			boolean b = this.getNextBool(boolstack);
			boolstack.push(b);
			boolstack.push(a);
			boolstack.push(b);
		}
		public String toString(){
			return "overbool";
		}
	};
	
	private static Operator op_true = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(true);
		}
		public String toString(){
			return "true";
		}
	};
	
	private static Operator op_false = new Operator() {
		public void execute(int[] memory, Stack<Integer> intstack,
				Stack<Boolean> boolstack) {
			boolstack.push(false);
		}
		public String toString(){
			return "false";
		}
	};
	
	public static Operator getOperator(String name){
		if(name.equals("abs")){
			return DefaultOperators.abs;
		}
		if(name.equals("add")){
			return DefaultOperators.add;
		}
		if(name.equals("and")){
			return DefaultOperators.and;
		}
		if(name.equals("angle")){
			return DefaultOperators.angle;
		}
		if(name.equals("%=")){
			return DefaultOperators.approx_equal;
		}
		if(name.equals("!%=")){
			return DefaultOperators.approx_not_equal;
		}
		if(name.equals("&")){
			return DefaultOperators.BW_and;
		}
		if(name.equals("~")){
			return DefaultOperators.BW_compl;
		}
		if(name.equals("<<")){
			return DefaultOperators.BW_left;
		}
		if(name.equals("|")){
			return DefaultOperators.BW_or;
		}
		if(name.equals(">>")){
			return DefaultOperators.BW_right;
		}
		if(name.equals("^")){
			return DefaultOperators.BW_xor;
		}
		if(name.equals("ceil")){
			return DefaultOperators.ceil;
		}
		if(name.equals("clear")){
			return DefaultOperators.clear;
		}
		if(name.equals("clearbool")){
			return DefaultOperators.clearbool;
		}
		if(name.equals("cond")){
			return DefaultOperators.cond;
		}
		if(name.equals("~=")){
			return DefaultOperators.custom_approx_equal;
		}
		if(name.equals("!~=")){
			return DefaultOperators.custom_approx_not_equal;
		}
		if(name.equals("dec")){
			return DefaultOperators.dec;
		}
		if(name.equals("--")){
			return DefaultOperators.decval;
		}
		if(name.equals("div")){
			return DefaultOperators.div;
		}
		if(name.equals("drop")){
			return DefaultOperators.drop;
		}
		if(name.equals("dropbool")){
			return DefaultOperators.dropbool;
		}
		if(name.equals("dup")){
			return DefaultOperators.dup;
		}
		if(name.equals("dupbool")){
			return DefaultOperators.dupbool;
		}
		if(name.equals("=")){
			return DefaultOperators.equal;
		}
		if(name.equals("floor")){
			return DefaultOperators.floor;
		}
		if(name.equals(">")){
			return DefaultOperators.greater;
		}
		if(name.equals(">=")){
			return DefaultOperators.greater_or_equal;
		}
		if(name.equals("inc")){
			return DefaultOperators.inc;
		}
		if(name.equals("++")){
			return DefaultOperators.incval;
		}
		if(name.equals("<")){
			return DefaultOperators.less;
		}
		if(name.equals("<=")){
			return DefaultOperators.less_or_equal;
		}
		if(name.equals("mod")){
			return DefaultOperators.mod;
		}
		if(name.equals("mult")){
			return DefaultOperators.mult;
		}
		if(name.equals("-")){
			return DefaultOperators.neg;
		}
		if(name.equals("not")){
			return DefaultOperators.not;
		}
		if(name.equals("!=")){
			return DefaultOperators.not_equal;
		}
		if(name.equals("false")){
			return DefaultOperators.op_false;
		}
		if(name.equals("true")){
			return DefaultOperators.op_true;
		}
		if(name.equals("or")){
			return DefaultOperators.or;
		}
		if(name.equals("over")){
			return DefaultOperators.over;
		}
		if(name.equals("overbool")){
			return DefaultOperators.overbool;
		}
		if(name.equals("pow")){
			return DefaultOperators.pow;
		}
		if(name.equals("pyth")){
			return DefaultOperators.pyth;
		}
		if(name.equals("rnd")){
			return DefaultOperators.rnd;
		}
		if(name.equals("sgn")){
			return DefaultOperators.sgn;
		}
		if(name.equals("sqr")){
			return DefaultOperators.sqr;
		}
		if(name.equals("start")){
			return DefaultOperators.start;
		}
		if(name.equals("stop")){
			return DefaultOperators.stop;
		}
		if(name.equals("store")){
			return DefaultOperators.store;
		}
		if(name.equals("sub")){
			return DefaultOperators.sub;
		}
		if(name.equals("swap")){
			return DefaultOperators.swap;
		}
		if(name.equals("swapbool")){
			return DefaultOperators.swapbool;
		}
		if(name.equals("xor")){
			return DefaultOperators.xor;
		}
		//default operator is 0
		return getIntOperator(0);
	}
}
